package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Creared with IntelliJ IDEA.
 * Description:
 * User:yxd
 * Date:2022-04-12
 * Time:20:53
 */
public class TCPThreadPoolEchoServer {
    private ServerSocket serverSocket = null;



    public TCPThreadPoolEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动!");
        ExecutorService pool = Executors.newCachedThreadPool();
        while(true){
            //由于TCP是有连接的,不能一上来就直接读取请求,得先建立连接(也是相当于接电话)
            //accept就是在接电话,而接电话的前提是有人给你打电话 了,如果没有客户端尝试建立连接,那么服务器就会陷入阻塞状态
            //accept是返回了一个Socket对象,称为clientSocket,后续如果和客户端之间沟通都是通过clientSocket来完成的,而serverSocket就只做了第一步也就是只接了个电话
            Socket clientSocket = serverSocket.accept();
            //但是在这里如果有多个客户端的话,这个processConnection内层循环无法结束,那么外层循环就无法连接其他客户端,因此在这里就需要处理一下,使用多线程或者线程池来解决
            //然后再处理响应
            //通过线程池来解决
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    processConnection(clientSocket);//这样之后每一个客户端的连接就从串行变成并行的了,然后每一个客户端的连接就可以成功了

                }
            });




        }
    }

    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s,%d] 客户端建立连接\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());//IP地址和端口号
        //接下来就是处理请求和响应
        //这里针对TCP socket的读写就和读写文件是一摸一样的
        try(InputStream inputStream = clientSocket.getInputStream()){
            try (OutputStream outputStream = clientSocket.getOutputStream()){
                Scanner scanner = new Scanner(inputStream);//从控制台写入到inputStream中去

                while (true){
                    //循环处理每个请求分别返回请求
                    if(!scanner.hasNext()){
                        System.out.printf("[%s,%d] 客户端断开连接!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                        break;
                    }
                    //此处用scanner更方便,如果使用inputStream的read也是可以的
                    String request = scanner.next();
                    //根据请求计算响应
                    String response = process(request);
                    //把这个响应返回给客户端
                    //为了方便起见,可以使用PrintWriter把OutputStream包裹一下
                    PrintWriter printWriter = new PrintWriter(outputStream);//填充性参数
                    printWriter.println(response);
                    //刷新缓冲区,如果没有这个刷新,可能客户端就不能第一时间看到响应结果
                    printWriter.flush();
                    System.out.printf("[%s,%d] req :%s,resp:%s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                clientSocket.close();//但是这个是每个连接有一个的,这个的数目很多,并且连接断开也就不用了,因此这个是会持续积累的,所以每次都得保证处理完的连接都进行释放
                //记得关闭文件(释放资源)   那为什么在UDP和serverSocket里面不需要对文件进行关闭呢  对于UDP的程序和serverSocket来说这些socket都是贯穿程序始终的,这些资源最迟也就是跟随进程一起被释放了(进程是系统分配资源的最小单位)
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TCPThreadPoolEchoServer server = new TCPThreadPoolEchoServer(9090);
        server.start();
    }
}
